package com.xm.servlet;

import com.alibaba.fastjson.JSON;
import com.xm.annotation.XmController;
import com.xm.annotation.XmRequestMapping;
import com.xm.constant.DispatcherConstant;
import com.xm.container.BeanFactoryAware;
import com.xm.container.FlagPar;
import com.xm.container.handler.HandlerEntity;
import com.xm.utils.ObjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author 黄家伟
 * @version v1.0.0
 * @mail huangwei_xm@163.com
 * @copyright <a href="http://www.huangwei.work">oneself</a>
 * @description DispatcherServlet描述如下
 * <pre>
 *    前端控制器
 * </pre>
 * @since 2021/4/24 19:05
 */
public class DispatcherServlet extends HttpServlet implements DispatcherConstant, BeanFactoryAware {

    private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);

    /**
     * 配置文件配置
     */
    private Properties properties = new Properties();
    ;
    /**
     * 全限定类名列表
     */
    private List<String> fullyClassNameList = new ArrayList<String>();

    private Map<String, HandlerEntity> handlerAdapter = new HashMap<String, HandlerEntity>();


    @Override
    public void init(ServletConfig config) throws ServletException {
        // 加载配置文件
        doProperties(config.getInitParameter(configLocation));
        // 扫描包
        doScanPackage(properties.getProperty(baseScan));
        // 扫描相关类中的注解、管理、注入
        initContainer();
        // 构造映射处理器HandlerMapping
        handlerAdapter();
        // 等待请求，处理请求
        System.out.println("测试");
    }

    private void handlerAdapter() {
        fullyClassNameList.forEach(fullyClassName -> {
            try {
                Class<?> aClass = Class.forName(fullyClassName);
                if (ObjectUtil.isNotAnnotationAndInterface(aClass)) {
                    // 非注解，非接口
                    if (aClass.isAnnotationPresent(XmController.class) && aClass.isAnnotationPresent(XmRequestMapping.class)) {
                        // 存在路由
                        XmRequestMapping cMapping = aClass.getAnnotation(XmRequestMapping.class);
                        Method[] methods = aClass.getMethods();
                        for (int i = 0; i < methods.length; i++) {
                            Method method = methods[i];
                            if (!method.isAnnotationPresent(XmRequestMapping.class)) {
                                continue;
                            }
                            XmRequestMapping mMapping = method.getAnnotation(XmRequestMapping.class);
                            FlagPar<String, Boolean> beanPar = getBeanName(aClass);
                            HandlerEntity handlerEntity = new HandlerEntity();
                            handlerEntity.setHandler(beanPar.getLeft());
                            handlerEntity.setMethod(method);
                            handlerEntity.setUri(cMapping.value() + mMapping.value());
                            handlerAdapter.put(handlerEntity.getUri(),handlerEntity);
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                log.error("没有发现Class={}", fullyClassName);
            }
        });
    }

    /**
     * 容器管理
     */
    private void initContainer() {
        // initBean
        fullyClassNameList.forEach(fullyClassName -> {
            try {
                Class<?> aClass = Class.forName(fullyClassName);
                initBean(aClass);
            } catch (ClassNotFoundException e) {
                log.error("没有发现Class={}", fullyClassName);
            }
        });
        // initAutowired (beans)
        fullyClassNameList.forEach(fullyClassName -> {
            try {
                Class<?> aClass = Class.forName(fullyClassName);
                initAutowired(aClass);
            } catch (ClassNotFoundException e) {
                log.error("没有发现Class={}", fullyClassName);
            }

        });
    }

    /**
     * 扫描包
     *
     * @param pack
     */
    private void doScanPackage(String pack) {
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        String scanPack = path + pack.replace(spot, sprit);
        File fileDir = new File(scanPack);
        File[] files = fileDir.listFiles();
        for (int i = 0; i < files.length; i++) {
            log.info(files[i].getName());
            if (files[i].isDirectory()) {
                // 目录
                String newPack = pack + spot + files[i].getName();
                doScanPackage(newPack);
            } else if (files[i].isFile() && files[i].getName().endsWith(classEndWith)) {
                // class文件
                String fullyClassName = pack + spot + files[i].getName().replace(classEndWith, noneStr);
                fullyClassNameList.add(fullyClassName);
            }
        }
    }

    /**
     * 加载配置文件
     *
     * @param initParameter 初始化参数配置文件名称
     */
    private void doProperties(String initParameter) {
        try {
            log.info(initParameter);
            InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(initParameter);
            properties.load(resourceAsStream);
        } catch (IOException e) {
            log.error("配置文件加载失败，请您检查。");
            e.printStackTrace();
        }
    }


    // 接收处理请求
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        String requestURI = req.getRequestURI();
        FlagPar<Object, Boolean> flagPar = chainFilter(requestURI);
        if (flagPar.getRight()) {
            // @ResponseBody 判定是否json
            resp.setHeader("Content-Type","application/json,charset=UTF-8");
            resp.getWriter().println(JSON.toJSON(flagPar.getLeft()));
        } else {
            resp.getWriter().println("404 not found interface");
        }
    }

    private FlagPar<Object, Boolean> chainFilter(String requestURI) {
        HandlerEntity handlerEntity = handlerAdapter.get(requestURI);
        FlagPar<Object, Boolean> flagPar = new FlagPar<Object, Boolean>(null,false);
        if (ObjectUtil.isNotEmpty(handlerEntity)) {
            Object bean = getBean(handlerEntity.getHandler());
            try {
                Object result = handlerEntity.getMethod().invoke(bean, null);
                flagPar.setLeft(result);
                flagPar.setRight(true);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }
        return flagPar;
    }

}
